#ifndef STX_LOG_SYSLOG_LOGGER_HPP
#define STX_LOG_SYSLOG_LOGGER_HPP

#include <stx/log/basic_logger.hpp>

#ifdef STX_HAS_SYSLOG
#include <syslog.h>
#include <sstream>

namespace stx {

//todo: Maybe we should add more log levels to fully support syslog priorities.
/*
    log_level_all,    
    log_level_trace,  LOG_DEBUG
    log_level_debug,  LOG_DEBUG
    log_level_info,   LOG_INFO
    log_level_warn,   LOG_WARNING / LOG_NOTICE
    log_level_error,  LOG_ERR
    log_level_fatal,  LOG_CRIT / LOG_ALERT / LOG_EMERG
    log_level_none    
*/
//  Additional log levels.
//enum {
//    log_level_alert = log_level_fatal + 1,
//    log_level_emergency
//};

class syslog_logger: public basic_logger
{
public:
    
    syslog_logger(
        const std::string& delimiter = "",
        int log_level = log_level_all):
            basic_logger(delimiter, log_level)
    {
    }
    
    //  options:
    //      LOG_PID: Log the process ID with each message. This is useful for
    //          identifying specific processes.
    //      LOG_CONS: Write messages to the system console if they cannot be
    //          sent to the logging facility. The syslog() function ensures that
    //          the process does not acquire the console as a controlling terminal
    //          in the process of writing the message.
    //      LOG_NDELAY: Open the connection to the logging facility immediately.
    //          Normally the open is delayed until the first message is logged.
    //          This is useful for programs that need to manage the order in
    //          which file descriptors are allocated.
    //      LOG_ODELAY: Delay open until syslog() is called.
    //      LOG_NOWAIT: Do not wait for child processes that may have been created
    //          during the course of logging the message. This option should be
    //          used by processes that enable notification of child termination
    //          using SIGCHLD, since syslog() may otherwise block waiting for a
    //          child whose exit status has already been collected.
    //  facility:
    //      LOG_USER: Messages generated by arbitrary processes. This is the
    //          default facility identifier if none is specified.
    //      LOG_LOCAL0: Reserved for local use.
    //      LOG_LOCAL1: Reserved for local use.
    //      LOG_LOCAL2: Reserved for local use.
    //      LOG_LOCAL3: Reserved for local use.
    //      LOG_LOCAL4: Reserved for local use.
    //      LOG_LOCAL5: Reserved for local use.
    //      LOG_LOCAL6: Reserved for local use.
    //      LOG_LOCAL7: Reserved for local use.
    syslog_logger(
        const std::string& prepending_string = "",
        int options = LOG_ODELAY,
        int facility = LOG_USER,
        const std::string& delimiter = "",
        int log_level = log_level_all)
    {
        create(prepending_string, options, facility, delimiter, log_level);
    }
    
    void create(
        const std::string& prepending_string = "",
        int options = LOG_ODELAY,
        int facility = LOG_USER,
        const std::string& delimiter = "",
        int log_level = log_level_all)
    {
        level_ = log_level;
        delimiter_ = delimiter;
        openlog(prepending_string.c_str(), options, facility);
    }
    
    virtual ~syslog_logger()
    {
        closelog();
    }
    
    syslog_logger& set_level(int new_level)
    {
        basic_logger::set_level(new_level);
        setlogmask(level_to_syslog_priority(new_level));
        return *this;
    }
    
    std::ostream& stream()
    {
        ss_.str("");
        return ss_;
    }
    
    void start_formatting(int message_level)
    {
        message_level_ = message_level;
    }
    
    void finish_formatting(log_formatter& fmt)
    {
        if (fmt.enabled()) {
            stream() << std::endl;
            syslog(level_to_syslog_priority(message_level_), "%s", ss_.str().c_str());
        }
    }
    
protected:
    
    int level_to_syslog_priority(int level)
    {
        int syslog_priority = LOG_DEBUG;
        switch (level) {
        case log_level_trace: syslog_priority = LOG_DEBUG;   break;
        case log_level_debug: syslog_priority = LOG_DEBUG;   break;
        case log_level_info:  syslog_priority = LOG_INFO;    break;
        case log_level_warn:  syslog_priority = LOG_WARNING; break;
        case log_level_error: syslog_priority = LOG_DEBUG;   break;
        case log_level_fatal: syslog_priority = LOG_CRIT;    break;
        }
        return syslog_priority;
    }
    
    std::ostringstream ss_;
    int message_level_;
};

} // namespace stx

#endif // STX_HAS_SYSLOG

#endif // STX_LOG_SYSLOG_LOGGER_HPP
